repo: Call fdatasync() before adding objects to the repo
authorColin Walters <walters@verbum.org>
Mon, 27 Aug 2012 15:53:06 +0000 (11:53 -0400)
committerColin Walters <walters@verbum.org>
Mon, 27 Aug 2012 19:35:40 +0000 (15:35 -0400)
commit2396608754060e9e7a68843b65e4d3b8f4a4b5f2
tree25189d7c855400aa4f7dbf4a0699e6a7c4aa4704
parent5038a1930f1779d5ba09cd24085ff6984eb052c5
repo: Call fdatasync() before adding objects to the repo

I run builds on my laptop, but it also crashes about 1/4 of the time
while suspending.  It's definitely undesrirable to get e.g. empty
.dirtree objects because they corrupt builds.  Concretely, I was
getting empty contents committed for xorg-util-macros.

Now, we used to write out temporary files using g_file_replace() which
does a fsync() during close, but then switched to a more "manual"
g_file_append_to().

We could switch back to g_file_replace(), but the problem is, we don't
want to call fsync() on temporary files in the case where we already
have the object.  Attempting to add an object we already have is a
*very* common case.

This is both the old and new code sequence for the case where an
object is already stored:

open(temp, O_WRONLY)
write() write() write()
close()
lstat(objects/3a/9fe332...) = 0
unlink(temp)

In the *new* code, here's the case where an object *isn't* stored:

open(temp, O_WRONLY)
write() write() write()
close()
lstat(objects/3a/9fe332...) = -1
open(temp, O_RDONLY)
fdatasync()
close()
rename(temp, objects/3a/9fe332)

Compare with the *old* code path for when an object isn't stored:

open(temp, O_WRONLY)
write() write() write()
close()
lstat(objects/3a/9fe332...) = -1
link(temp, objects/3a/9fe332)
unlink(temp)

The problem with this is we really need to fdatasync().  Also doing
just rename() instead of the weird link()/unlink() helps us express to
the filesystem that we want atomic semantics.  For example, BTRFS has
special handling for rename().
src/libostree/ostree-repo.c
src/libotutil/ot-unix-utils.c
src/libotutil/ot-unix-utils.h